package com.feature.core.config;

import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson2.JSON;
import com.feature.core.annotation.ProcessLog;

import jakarta.servlet.http.HttpServletRequest;

@Aspect
public class LogAspectConfig implements Ordered {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("execution(* com.feature.shop..*.service.*.*(..))||execution(* com.feature.shop..*.controller.*.*(..))||execution(* com.feature.shop..*.apiimpl.*.*(..)) ")
    public void serviceMethodPointcut() {
    }

    @Pointcut("@annotation(com.feature.core.annotation.ProcessLog)")
    public void processLogPointCut() {

    }

    @Around("processLogPointCut() && !serviceMethodPointcut()")
    public Object aroundProcessLogMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        return log(joinPoint);
    }

    @Around("serviceMethodPointcut()")
    public Object aroundServiceMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        return log(joinPoint);
    }

    private Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        ProcessLogVo processLog = getProcessLog(joinPoint);
        String param = "";
        try {
            if (processLog.logRequest) {
                Object[] args = joinPoint.getArgs();
                List<Object> list = Stream.of(args).filter(v -> v != null).filter(v -> !HttpServletRequest.class.isAssignableFrom(v.getClass()) && !MultipartFile.class.isAssignableFrom(v.getClass()))
                        .collect(Collectors.toList());
                param = JSON.toJSONString(list);
            }
        } catch (Exception e) {
            logger.error("", e);
        }
        String methodName = getLogMethodName(joinPoint);
        logger.info("method:{}开始,参数:{}", methodName, param);
        try {
            Object result = joinPoint.proceed();
            logger.info("method:{}结束,响应:{}", methodName, processLog.logResposne ? JSON.toJSONString(result) : "");
            return result;
        } catch (Exception e) {
            logger.info("method:{}结束,异常:{}", methodName, e.getMessage());
            throw e;
        }
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 1;
    }

    private String getLogMethodName(ProceedingJoinPoint joinPoint) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            return method.getDeclaringClass().getSimpleName() + "." + method.getName();
        } catch (Exception e) {
            logger.error("", e);
        }
        return "";
    }

    private ProcessLogVo getProcessLog(ProceedingJoinPoint joinPoint) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            ProcessLog processLog = null;
            if (method.isAnnotationPresent(ProcessLog.class)) {
                processLog = method.getDeclaredAnnotation(ProcessLog.class);
            }
            if (processLog == null && method.getDeclaringClass().isAnnotationPresent(ProcessLog.class)) {
                processLog = method.getDeclaringClass().getDeclaredAnnotation(ProcessLog.class);
            }
            if (processLog != null) {
                return new ProcessLogVo(processLog.logRequest(), processLog.logResponse());
            }
            return new ProcessLogVo(true, true);
        } catch (Exception e) {
            logger.error("", e);
            return new ProcessLogVo(true, false);
        }
    }

    record ProcessLogVo(boolean logRequest, boolean logResposne) {
    }
}
